
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

// A third-party license is embedded for some of the code in this file:
// Some formulas were originally copied from "d3.js" with some
// modifications made for this project.
// (See more details in the comment of the method "step" below.)
// The use of the source code of this file is also subject to the terms
// and consitions of the license of "d3.js" (BSD-3Clause, see
// </licenses/LICENSE-d3>).
import * as vec2 from 'zrender/lib/core/vector.js';
var scaleAndAdd = vec2.scaleAndAdd; // function adjacentNode(n, e) {
//     return e.n1 === n ? e.n2 : e.n1;
// }

export function forceLayout(inNodes, inEdges, opts) {
	var nodes = inNodes;
	var edges = inEdges;
	var rect = opts.rect;
	var width = rect.width;
	var height = rect.height;
	var center = [rect.x + width / 2, rect.y + height / 2]; // let scale = opts.scale || 1;

	var gravity = opts.gravity == null ? 0.1 : opts.gravity; // for (let i = 0; i < edges.length; i++) {
	//     let e = edges[i];
	//     let n1 = e.n1;
	//     let n2 = e.n2;
	//     n1.edges = n1.edges || [];
	//     n2.edges = n2.edges || [];
	//     n1.edges.push(e);
	//     n2.edges.push(e);
	// }
	// Init position

	for (var i = 0; i < nodes.length; i++) {
		var n = nodes[i];

		if (!n.p) {
			n.p = vec2.create(width * (Math.random() - 0.5) + center[0], height * (Math.random() - 0.5) + center[1]);
		}

		n.pp = vec2.clone(n.p);
		n.edges = null;
	} // Formula in 'Graph Drawing by Force-directed Placement'
	// let k = scale * Math.sqrt(width * height / nodes.length);
	// let k2 = k * k;

	var initialFriction = opts.friction == null ? 0.6 : opts.friction;
	var friction = initialFriction;
	var beforeStepCallback;
	var afterStepCallback;
	return {
		warmUp: function () {
			friction = initialFriction * 0.8;
		},
		setFixed: function (idx) {
			nodes[idx].fixed = true;
		},
		setUnfixed: function (idx) {
			nodes[idx].fixed = false;
		},

		/**
     * Before step hook
     */
		beforeStep: function (cb) {
			beforeStepCallback = cb;
		},

		/**
     * After step hook
     */
		afterStep: function (cb) {
			afterStepCallback = cb;
		},

		/**
     * Some formulas were originally copied from "d3.js"
     * https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/layout/force.js
     * with some modifications made for this project.
     * See the license statement at the head of this file.
     */
		step: function (cb) {
			beforeStepCallback && beforeStepCallback(nodes, edges);
			var v12 = [];
			var nLen = nodes.length;

			for (var i = 0; i < edges.length; i++) {
				var e = edges[i];

				if (e.ignoreForceLayout) {
					continue;
				}

				var n1 = e.n1;
				var n2 = e.n2;
				vec2.sub(v12, n2.p, n1.p);
				var d = vec2.len(v12) - e.d;
				var w = n2.w / (n1.w + n2.w);

				if (isNaN(w)) {
					w = 0;
				}

				vec2.normalize(v12, v12);
				!n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction);
				!n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction);
			} // Gravity

			for (var i = 0; i < nLen; i++) {
				var n = nodes[i];

				if (!n.fixed) {
					vec2.sub(v12, center, n.p); // let d = vec2.len(v12);
					// vec2.scale(v12, v12, 1 / d);
					// let gravityFactor = gravity;

					scaleAndAdd(n.p, n.p, v12, gravity * friction);
				}
			} // Repulsive
			// PENDING

			for (var i = 0; i < nLen; i++) {
				var n1 = nodes[i];

				for (var j = i + 1; j < nLen; j++) {
					var n2 = nodes[j];
					vec2.sub(v12, n2.p, n1.p);
					var d = vec2.len(v12);

					if (d === 0) {
						// Random repulse
						vec2.set(v12, Math.random() - 0.5, Math.random() - 0.5);
						d = 1;
					}

					var repFact = (n1.rep + n2.rep) / d / d;
					!n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact);
					!n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact);
				}
			}

			var v = [];

			for (var i = 0; i < nLen; i++) {
				var n = nodes[i];

				if (!n.fixed) {
					vec2.sub(v, n.p, n.pp);
					scaleAndAdd(n.p, n.p, v, friction);
					vec2.copy(n.pp, n.p);
				}
			}

			friction = friction * 0.992;
			var finished = friction < 0.01;
			afterStepCallback && afterStepCallback(nodes, edges, finished);
			cb && cb(finished);
		}
	};
}